---
import { extractTabsData, getTabId } from './utils';
import { type TabHeaderLink } from './types.d';
import { OpenInCTA } from '@components/open-in-cta/OpenInCTA';
import styles from '@design-system/modules/Tabs.module.scss';

interface Props {
    tabItemIdPrefix?: string,
    headerLinks?: TabHeaderLink[]
}

const html = await Astro.slots.render('default');
// Extract tabs data so that the tab nav items can be rendered at build time
const tabsData = extractTabsData(html);

const { tabItemIdPrefix, headerLinks } = Astro.props as Props;
---

<tabs-with-html-children data-tabs-data={JSON.stringify(tabsData)} data-tab-Item-id-prefix={tabItemIdPrefix}>
    <div class:list={['tabs-outer', styles.tabsOuter]}>
        <header class={'tabs-header'}>
            <ul class="tabs-nav-list" role="tablist">
                {
                    tabsData.map(({ id, label }) => {
                        const tabId = getTabId({ id: id!, prefix: tabItemIdPrefix });
                        return (
                            <li id={tabId} class="tabs-nav-item" role="presentation">
                                <button class:list={['button-style-none', 'tabs-nav-link']} role="tab">
                                    {label}
                                </button>
                            </li>
                        );
                    })
                }
            </ul>

            {headerLinks && <div class={styles.externalLinks}>{
                headerLinks.map(({ type, ...props }) => {
                    if (type === 'codesandbox') {
                        return <OpenInCTA type="codesandbox" {...props} />
                    }
                })
            }</div>}
        </header>
        <div class="tabs-content" role="tabpanel">
            <Fragment set:html={html} />
        </div>
    </div>
</tabs-with-html-children>

<script>
    import { TAB_ID_PROP } from './constants';
    import type { TabData } from './types';
    import { getTabId } from './utils';
    import { navigate, browserHistory } from '@utils/navigation';

    const TAB_BUTTON_SELECTOR = '.tabs-nav-item button';
    const TABS_CONTENT_SELECTOR = '.tabs-content';

    class TabsWithHtmlChildren extends HTMLElement {
        tabsData: TabData[];
        selectedIndex: number;
        tabItemIdPrefix: string;
        unlistenToHashChanges: () => void;

        constructor() {
            super();

            this.tabItemIdPrefix = this.dataset.tabItemIdPrefix || '';
            this.tabsData = this.dataset.tabsData ? JSON.parse(this.dataset.tabsData) : [];

            const hashTabIndex = this.getTabIndexFromHash(window.location?.hash);
            this.selectedIndex = hashTabIndex >= 0 ? hashTabIndex : 0;

            this.initNavButtonHandlers();
            this.updateTabs();

            this.unlistenToHashChanges = browserHistory?.listen(({ location }) => {
                const hashTabIndex = this.getTabIndexFromHash(location?.hash);
                if (hashTabIndex >= 0) {
                    this.selectedIndex = hashTabIndex;
                    this.updateTabs();
                }
            })!;
        }

        get selected(): TabData {
            return this.tabsData[this.selectedIndex];
        }

        disconnectedCallback() {
            this.unlistenToHashChanges();
        }

        getTabIndexFromHash(hash: string) {
            if (!hash) {
                return -1;
            }

            return this.tabsData.findIndex((t) => hash.startsWith(`#${this.tabItemIdPrefix}${t.id}`));
        }

        initNavButtonHandlers() {
            this.querySelectorAll(TAB_BUTTON_SELECTOR).forEach((button, index) => {
                button.addEventListener('click', () => {
                    this.selectedIndex = index;
                    this.updateTabs();

                    const tabId = getTabId({ id: this.selected.id, prefix: this.tabItemIdPrefix });
                    navigate(`#${tabId}`);
                });
            });
        }

        updateTabs() {
            // Nav item buttons
            const buttons = this.querySelectorAll(TAB_BUTTON_SELECTOR);
            buttons.forEach((el) => (el as HTMLElement).classList.remove('active'));
            buttons[this.selectedIndex].classList.add('active');

            // Nav content
            this.querySelectorAll(`[${TAB_ID_PROP}]`).forEach((el) => ((el as HTMLElement).style.display = 'none'));
            const selectedTab = this.querySelector(`[${TAB_ID_PROP}="${this.selected.id}"]`);
            if (selectedTab) {
                (selectedTab as HTMLElement).style.display = 'block';
            }

            // Nav content labeledby
            const label = `${this.selected.label} tab`;
            this.querySelector(TABS_CONTENT_SELECTOR)?.setAttribute('aria-labelledby', label);
        }
    }

    customElements.define('tabs-with-html-children', TabsWithHtmlChildren);
</script>
